OS: Windows 10
Editor: Visual Studio Code
Rust version: 1.63.0
如同許多程式語言,都會有幾個內建的Key-value pair的集合型別,今天要來試試看Rust語言中的雜湊表(Hash map)。
首先是建立與加入新的資料,我們要先加入這個模組,在main.rs
的最上面:
use std::collections::HashMap;
建立的方式有幾種,第一種,最平常的:
let mut scores = Hash::new();
scores.insert(String::from("RED"), 10);
scores.insert(String::from("BLUE"), 50);
可以看到上面的寫法沒有先定義scores
的key-value
型別是甚麼,直到加入資料讓compiler自己判斷,上面定義的Hash Map型別是HashMap<String, i32>
。
再來我們可以用兩個同樣大小的向量(vector),蒐集成一個Hash Map:
let teams = vec![String::from("RED"), String::from("BLUE")];
let initial_scores = vec![10, 50];
let mut scores: HashMap<_, _> = teams.into_iter().zip(initial_scores.into_iter()).collect();
可以發現,這個做法需要提前告訴compilercollect()
出來的型別是Hash Map,所以要加上HashMap<_, _>
,當然也可以直接定義HashMap<String, i32>
,使用HashMap<_, _>
可以讓compiler自行判斷我們加入的型別是甚麼。
如何取得Hash Map中的資料:
let blue_team_score = scores.get("BLUE");
match blue_team_score {
Some(&score) => println!("BLUE team score is {}", score),
None => println!("There is no BLUE team"),
};
跟向量(vector)一樣,回傳的是Option<T>
,要開發者去處理,有值跟跟沒值的情況。
// 元組(tuple)取得key跟value
for (key, val) in &scores {
println!("Team {} got {}.", key, val);
}
更新Hash map的時候,會發生一個一問題,Key是不是已經存在Hash Map裡面了。當然,我們可以再次使用insert
,但這會把原本的值蓋掉:
scores.insert(String::from("BLUE"), 50);
scores.insert(String::from("BLUE"), 20); // 現在BLUE強制被改成20了
如果只是要單純檢查有沒有Key,可以使用contains_key
:
println!("Is red team exists > {}", scores.contains_key("RED"));
更新的話可以使用get_mut
來更新資料:
match scores.get_mut("BLUE") {
Some(score) => {
*score = 30;
println!("BLUE team score is {}", score);
}
None => {
scores.insert(String::from("BLUE"), 30);
println!("Add new team, BLUE");
}
};
或是有一個更漂亮的寫法:
// BLUE隊直接設成30分
let blue_team_score = scores.entry(String::from("BLUE")).or_insert(0);
*blue_team_score = 30;
or_insert
這個會把不存在Key加入一個新的值,但如果Key已經存在了,則不會把已經存在的值蓋過去。
然後在or_insert
之前有一個entry
,這會把Key-value pair整個回傳回來,我們可以試著這樣改寫,雖然看起來更複雜,但嘗試看看entry
展開來的樣子:
use std::collections::{
hash_map::Entry::{Occupied, Vacant},
HashMap,
};
// ....
let blue_team_score = match scores.entry(String::from("BLUE")) {
Occupied(entry) => entry.into_mut(),
Vacant(entry) => entry.insert(0),
};
*blue_team_score = 30;
標準函式庫有給兩種刪除的API,一樣會刪除(講廢話),但會傳的東西不同,請看:
// remove_entry
if let Some((team_name, score)) = scores.remove_entry("RED") {
println!("Delete team: {}, latest scrore: {}.", team_name, score);
} else {
println!("There is no team called RED.");
}
// remove
if let Some(score) = scores.remove("RED") {
println!("Delete team RED, latest scrore: {}.", score);
} else {
println!("There is no team called RED.");
}
看得出來,remove_entry
會回傳整個Key-value pair然後用Option<T>
包裝起來;remove
會回傳value,一樣是被Option<T>
包裝起來。